/**
 * MIT License
 * 
 * Copyright (c) 2017 CaiDongyu
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.tunnel.client.task;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

import com.tunnel.client.config.Config;
import com.tunnel.client.config.IpAndPort;
import com.tunnel.common.tcp.TcpBaseTask;
import com.tunnel.common.tunnel.TunnelDataQueueManager;

import io.netty.channel.ChannelHandlerContext;

/**
 * Tunnel与客户服务之间的通讯任务 在tunnel-client接受到来自tunnel-server的请求之后
 */
public class TcpTask extends TcpBaseTask {

	private int portIndex;
	private IpAndPort ipAndPort;
	private String connectionId;
	private byte[] connectionIdBytes;
	private ChannelHandlerContext ctx;
	private Socket socket;
	private InputStream ioIn;
	private OutputStream ioOut;

	public IpAndPort getIpAndPort() {
		return ipAndPort;
	}
	
	public void cancelTask() {
//		LogUtil.log("task needstop: " + connectionId);
		if (!isStop()) {
			super.setStop(true);
			try {
				ioIn.close();
			} catch (Exception e) {
			}
			try {
				ioOut.close();
			} catch (Exception e) {
			}
			try {
				socket.close();
			} catch (Exception e) {
			}
		}
	}

	public String getConnectionId() {
		return connectionId;
	}
	
	public byte[] getConnectionIdBytes() {
		return connectionIdBytes;
	}

	public TcpTask(int portIndex, byte[] connectionIdBytes, ChannelHandlerContext ctx) {
		super();
		this.portIndex = portIndex;
		this.connectionIdBytes = connectionIdBytes;
		connectionId = new String(connectionIdBytes);
		this.ctx = ctx;
		this.setName("TcpTask-" + connectionId);
	}

	@Override
	public void run() {
		// LogUtil.log(requestId+" content: "+new String(dataBytes));
		// 新建连接
		if (portIndex < Config.TCP_LIST.size()) {
			ipAndPort = Config.TCP_LIST.get(portIndex);
			try {

				socket = new Socket();
				socket.connect(new InetSocketAddress(ipAndPort.getIp(), ipAndPort.getPort()), 1000);// 设置连接请求超时时间10
																									// s
				socket.setSoTimeout(30000);// 设置读操作超时时间30 s

				ioIn = socket.getInputStream();
				ioOut = socket.getOutputStream();
			} catch (Exception e) {
				// 连失败
				String error = e.getMessage() == null ? "socket connection "+ipAndPort.getIp()+":"+ipAndPort.getPort()+" failed" : e.getMessage();
				closeTcpConnection(error);
				
			}
		} else {
			// 连接失败
			closeTcpConnection("the portIndex "+portIndex+" not exit");
		}

		// LogUtil.log("stop task："+requestId);
	}

	@Override
	public boolean sendData2App(byte[] ...datas) {
		if(isStop()){
			return true;
		}
		if(ioOut == null){
			//说明还未建立连接
			return false;
		}
		try {
			for(byte[] data:datas){
//				LogUtil.log("send app data size:"+data.length);
				ioOut.write(data);
			}
			ioOut.flush();
			return true;
		} catch (Exception e) {
			String error = e.getMessage() == null ? "connection "+ipAndPort.getIp()+":"+ipAndPort.getPort()+" closed":e.getMessage();
			closeTcpConnection("send data 2 app,"+error);
			return true;
		}
	}
	
	public void closeTcpConnection(String msg){
//		LogUtil.log(msg);
		TunnelDataQueueManager.commitData(this, TunnelDataQueueManager.TCP_CLOSE_MSG, ctx, connectionIdBytes,
				msg.getBytes());
	}

	public int available() throws IOException{
		return ioIn==null?0:ioIn.available();
	}

	public void read(byte[] data) throws IOException{
//		LogUtil.log("read app data size:"+data.length);
		ioIn.read(data);
	}
	
	public void sendUrgentData() throws IOException{
		socket.sendUrgentData(0xFF);
	}
	
}
